home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Payment / DTA.php
Encoding:
PHP Script  |  2005-12-02  |  19.5 KB  |  521 lines

  1. <?php
  2.  
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2003-2005 Hermann Stainer, Web-Gear                    |
  7. // | http://www.web-gear.com/                                             |
  8. // | All rights reserved.                                                 |
  9. // +----------------------------------------------------------------------+
  10. // | DTA is a class that provides functions to create DTA files used in   |
  11. // | Germany to exchange informations about money transactions with banks |
  12. // | or online banking programs.                                          |
  13. // | This LICENSE is in the BSD license style.                            |
  14. // |                                                                      |
  15. // | Redistribution and use in source and binary forms, with or without   |
  16. // | modification, are permitted provided that the following conditions   |
  17. // | are met:                                                             |
  18. // |                                                                      |
  19. // | Redistributions of source code must retain the above copyright       |
  20. // | notice, this list of conditions and the following disclaimer.        |
  21. // |                                                                      |
  22. // | Redistributions in binary form must reproduce the above copyright    |
  23. // | notice, this list of conditions and the following disclaimer in the  |
  24. // | documentation and/or other materials provided with the distribution. |
  25. // |                                                                      |
  26. // | Neither the name of Hermann Stainer, Web-Gear nor the names of his   |
  27. // | contributors may be used to endorse or promote products derived from |
  28. // | this software without specific prior written permission.             |
  29. // |                                                                      |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED   |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  41. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Hermann Stainer <hs@web-gear.com>                            |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: DTA.php,v 1.2.0 2005/01/25 15:00:00 hstainer Exp $
  47.  
  48.  
  49. /**
  50. * Determines the type of the DTA file:
  51. * DTA file contains credit payments.
  52. *
  53. * @const DTA_CREDIT
  54. */
  55. define("DTA_CREDIT", 0);
  56.  
  57. /**
  58. * Determines the type of the DTA file:
  59. * DTA file contains debit payments (default).
  60. *
  61. * @const DTA_DEBIT
  62. */
  63. define("DTA_DEBIT", 1);
  64.  
  65.  
  66. /**
  67. * Dta class provides functions to create and handle with DTA files
  68. * used in Germany to exchange informations about money transactions with
  69. * banks or online banking programs.
  70. *
  71. * @author   Hermann Stainer <hs@web-gear.com>
  72. * @version  $Revision: 0.81 $
  73. * @since    DTA 0.1
  74. */
  75. class DTA {
  76.     /**
  77.     * Type of DTA file, DTA_CREDIT or DTA_DEBIT.
  78.     *
  79.     * @var integer $type
  80.     */
  81.     var $type;
  82.  
  83.     /**
  84.     * Account data for the file sender.
  85.     *
  86.     * @var integer $account_file_sender
  87.     */
  88.     var $account_file_sender;
  89.  
  90.     /**
  91.     * Array of ASCII Codes of valid chars for DTA field data.
  92.     *
  93.     * @var array $validString_chars
  94.     */
  95.     var $validString_chars;
  96.  
  97.     /**
  98.     * Current timestamp.
  99.     *
  100.     * @var integer $timestamp
  101.     */
  102.     var $timestamp;
  103.  
  104.     /**
  105.     * Array of exchanges that the DTA file should contain.
  106.     *
  107.     * @var integer $exchanges
  108.     */
  109.     var $exchanges;
  110.  
  111.     /**
  112.     * Constructor. The type of the DTA file must be set. One file can
  113.     * only contain credits (DTA_CREDIT) OR debits (DTA_DEBIT).
  114.     * This is a definement of the DTA format.
  115.     *
  116.     * @param  integer $type Determines the type of the DTA file. Either DTA_CREDIT or DTA_DEBIT. Must be set.
  117.     * @access public
  118.     */
  119.     function DTA($type)
  120.     {
  121.         $this->type = $type;
  122.  
  123.         $this->account_file_sender = array();
  124.  
  125.         $this->validString_chars = array(32, 36, 37, 38, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 196, 214, 220, 223); 
  126.  
  127.         $this->timestamp = time();
  128.  
  129.         $this->exchanges = array();
  130.     }
  131.  
  132.     /**
  133.     * Checks if the given string contains only chars valid for fields in DTA files.
  134.     *
  135.     * @param  string  $string String that is checked.
  136.     * @access public
  137.     * @return boolean
  138.     */
  139.     function validString($string)
  140.     {
  141.         $occuring_chars = count_chars($string, 1);
  142.  
  143.         $result = true;
  144.  
  145.         foreach ($occuring_chars as $char_ord => $char_amount) {
  146.             if (!in_array($char_ord, $this->validString_chars)) {
  147.                 $result = false;
  148.                 break;
  149.             }
  150.         }
  151.  
  152.         return $result;
  153.     }
  154.  
  155.     /**
  156.     * Makes the given string valid for DTA files. German umlauts become uppercase,
  157.     * all other chars not allowed are replaced with space
  158.     *
  159.     * @param  string  $string String that should made valid.
  160.     * @access public
  161.     * @return string
  162.     */
  163.     function makeValidString($string)
  164.     {
  165.  
  166.         $result = "";
  167.  
  168.         if (strlen($string) > 0) {
  169.             $search = array(
  170.                 "'─'",
  171.                 "'╓'",
  172.                 "'▄'",
  173.                 "'Σ'",
  174.                 "'÷'",
  175.                 "'ⁿ'",
  176.                 "'▀'"
  177.             );
  178.  
  179.             $replace = array(
  180.                 "Ae",
  181.                 "Oe",
  182.                 "Ue",
  183.                 "ae",
  184.                 "oe",
  185.                 "ue",
  186.                 "ss"
  187.             );
  188.  
  189.             $result = strtoupper(preg_replace ($search, $replace, $string));
  190.  
  191.             for ($index = 0;$index < strlen($result);$index++) {
  192.                 if (!in_array(ord(substr($result, $index, 1)), $this->validString_chars)) {
  193.                     $result[$index] = " ";
  194.                 }
  195.             }
  196.         }
  197.  
  198.         return $result;
  199.     }
  200.  
  201.     /**
  202.     * Set the sender of the DTA file. Must be set for valid DTA file.
  203.     * The given account data is also used as default sender's account.
  204.     * Account data contains
  205.     *  name            Sender's name. Maximally 27 chars are allowed.
  206.     *  bank_code       Sender's bank code.
  207.     *  account_number  Sender's account number.
  208.     *  additional_name If necessary, additional line for sender's name (maximally 27 chars).
  209.  
  210.     * @param  array   $account          Account data fot file sender.
  211.     * @access public
  212.     * @return boolean
  213.     */
  214.     function setAccountFileSender($account)
  215.     {
  216.         if (strlen($account['name']) > 0 && is_numeric($account['bank_code']) && is_numeric($account['account_number'])) {
  217.             if (empty($account['additional_name'])) {
  218.                 $account['additional_name'] = "";
  219.             }
  220.  
  221.             $this->account_file_sender = array(
  222.                 "name"            => substr($this->makeValidString($account['name']), 0, 27),
  223.                 "bank_code"       => $account['bank_code'],
  224.                 "account_number"  => $account['account_number'],
  225.                 "additional_name" => substr($this->makeValidString($account['additional_name']), 0, 27)
  226.             );
  227.  
  228.             $result = true;
  229.         } else {
  230.             $result = false;
  231.         }
  232.  
  233.         return $result;
  234.     }
  235.  
  236.     /**
  237.     * Adds an exchange. First the account data for the receiver of the exchange is set.
  238.     * In the case the DTA file contains credits, this is the payment receiver. In the other
  239.     * case (the DTA file contains debits), this is the account, from which money is taken away.
  240.     * If the sender is not specified, values of the file sender are used by default.
  241.     * Account data for receiver and sender contain
  242.     *  name            Name. Maximally 27 chars are allowed.
  243.     *  bank_code       Bank code.
  244.     *  account_number  Account number.
  245.     *  additional_name If necessary, additional line for name (maximally 27 chars).
  246.     *
  247.     * @param  array   $account_receiver Receiver's account data.
  248.     * @param  double  $amount           Amount of money in this exchange. Currency: EURO
  249.     * @param  array   $purposes         Array of up to 15 lines (maximally 27 chars each) for description of the exchange.
  250.     * @param  array   $account_sender   Sender's account data.
  251.     * @access public
  252.     * @return boolean
  253.     */
  254.     function addExchange($account_receiver, $amount, $purposes, $account_sender = array())
  255.     {
  256.         if (empty($account_receiver['additional_name'])) {
  257.             $account_receiver['additional_name'] = "";
  258.         }
  259.         if (empty($account_sender['name'])) {
  260.             $account_sender['name'] = $this->account_file_sender['name'];
  261.         }
  262.         if (empty($account_sender['bank_code'])) {
  263.             $account_sender['bank_code'] = $this->account_file_sender['bank_code'];
  264.         }
  265.         if (empty($account_sender['account_number'])) {
  266.             $account_sender['account_number'] = $this->account_file_sender['account_number'];
  267.         }
  268.         if (empty($account_sender['additional_name'])) {
  269.             $account_sender['additional_name'] = $this->account_file_sender['additional_name'];
  270.         }
  271.  
  272.         if (strlen($account_sender['name']) > 0 && is_numeric($account_sender['bank_code']) && is_numeric($account_sender['account_number']) && strlen($account_receiver['name']) > 0 && is_numeric($account_receiver['bank_code']) && is_numeric($account_receiver['account_number']) && is_numeric($amount) && $amount > 0 && ((is_array($purposes) && count($purposes) >= 1) || (is_string($purposes) && strlen($purposes) > 0))) {
  273.  
  274.             $amount = round($amount * 100);
  275.  
  276.             if (is_string($purposes)) {
  277.                 $purposes = array($purposes, "");
  278.             }
  279.  
  280.             $purposes_data = $purposes;
  281.             $purposes = array();
  282.  
  283.             foreach ($purposes_data as $purpose) {
  284.                 $purposes[] = substr($this->makeValidString($purpose), 0, 27);
  285.             }
  286.  
  287.             $this->exchanges[] = array(
  288.                 "sender_name"              => substr($this->makeValidString($account_sender['name']), 0, 27),
  289.                 "sender_bank_code"         => $account_sender['bank_code'],
  290.                 "sender_account_number"    => $account_sender['account_number'],
  291.                 "sender_additional_name"   => substr($this->makeValidString($account_sender['additional_name']), 0, 27),
  292.                 "receiver_name"            => substr($this->makeValidString($account_receiver['name']), 0, 27),
  293.                 "receiver_bank_code"       => $account_receiver['bank_code'],
  294.                 "receiver_account_number"  => $account_receiver['account_number'],
  295.                 "receiver_additional_name" => substr($this->makeValidString($account_receiver['additional_name']), 0, 27),
  296.                 "amount"                   => $amount,
  297.                 "purposes"                 => $purposes
  298.             );
  299.  
  300.             $result = true;
  301.         } else {
  302.             $result = false;
  303.         }
  304.  
  305.         return $result;
  306.     }
  307.  
  308.     /**
  309.     * Returns the full content of the generated DTA file. All added exchanges are processed.
  310.     *
  311.     * @access public
  312.     * @return string
  313.     */
  314.     function getFileContent()
  315.     {
  316.         $content = "";
  317.  
  318.         $sum_account_numbers = 0;
  319.         $sum_bank_codes = 0;
  320.         $sum_amounts = 0;
  321.  
  322.         /**
  323.          * data record A
  324.          */
  325.  
  326.         // record length (128 Bytes)
  327.         $content .= str_pad ("128", 4, "0", STR_PAD_LEFT);
  328.         // record type
  329.         $content .= "A";
  330.         // file mode (credit or debit)
  331.         $content .= ($this->type == DTA_CREDIT) ? "G" : "L";
  332.         // Customer File ("K") / Bank File ("B")
  333.         $content .= "K";
  334.         // sender's bank code
  335.         $content .= str_pad ($this->account_file_sender['bank_code'], 8, "0", STR_PAD_LEFT);
  336.         // only used if Bank File, otherwise NULL
  337.         $content .= str_repeat ("0", 8);
  338.         // sender's name
  339.         $content .= str_pad ($this->account_file_sender['name'], 27, " ", STR_PAD_RIGHT);
  340.         // date of file creation
  341.         $content .= strftime("%d%m%y", $this->timestamp);
  342.         // free (bank internal)
  343.         $content .= str_repeat (" ", 4);
  344.         // sender's account number
  345.         $content .= str_pad ($this->account_file_sender['account_number'], 10, "0", STR_PAD_LEFT);
  346.         // sender's reference number (optional)
  347.         $content .= str_repeat ("0", 10);
  348.         // free (reserve)
  349.         $content .= str_repeat (" ", 15);
  350.         // execution date ("DDMMYYYY", optional)
  351.         $content .= str_repeat (" ", 8);
  352.         // free (reserve)
  353.         $content .= str_repeat (" ", 24);
  354.         // currency (1 = Euro)
  355.         $content .= "1";
  356.  
  357.         /**
  358.          * data record(s) C
  359.          */
  360.  
  361.         foreach ($this->exchanges as $exchange) {
  362.             $sum_account_numbers += $exchange['receiver_account_number'];
  363.             $sum_bank_codes += $exchange['receiver_bank_code'];
  364.             $sum_amounts += $exchange['amount'];
  365.  
  366.             $additional_purposes = $exchange['purposes'];
  367.             $first_purpose = array_shift($additional_purposes);
  368.  
  369.             $additional_parts = array();
  370.  
  371.             if (strlen($exchange['receiver_additional_name']) > 0) {
  372.                 $additional_parts[] = array("type" => "01",
  373.                     "content" => $exchange['receiver_additional_name']
  374.                     );
  375.             }
  376.  
  377.             foreach ($additional_purposes as $additional_purpose) {
  378.                 $additional_parts[] = array("type" => "02",
  379.                     "content" => $additional_purpose
  380.                     );
  381.             }
  382.  
  383.             if (strlen($exchange['sender_additional_name']) > 0) {
  384.                 $additional_parts[] = array("type" => "03",
  385.                     "content" => $exchange['sender_additional_name']
  386.                     );
  387.             }
  388.  
  389.             $additional_parts_number = count($additional_parts); 
  390.             // record length (187 Bytes + 29 Bytes for each additional part)
  391.             $content .= str_pad (187 + $additional_parts_number * 29, 4, "0", STR_PAD_LEFT); 
  392.             // record type
  393.             $content .= "C"; 
  394.             // first involved bank
  395.             $content .= str_pad ($exchange['sender_bank_code'], 8, "0", STR_PAD_LEFT); 
  396.             // receiver's bank code
  397.             $content .= str_pad ($exchange['receiver_bank_code'], 8, "0", STR_PAD_LEFT); 
  398.             // receiver's account number
  399.             $content .= str_pad ($exchange['receiver_account_number'], 10, "0", STR_PAD_LEFT); 
  400.             // internal customer number (11 chars) or NULL
  401.             $content .= "0" . str_repeat ("0", 11) . "0"; 
  402.             // payment mode (text key)
  403.             $content .= ($this->type == DTA_CREDIT) ? "51" : "05"; 
  404.             // additional text key
  405.             $content .= "000"; 
  406.             // bank internal
  407.             $content .= " "; 
  408.             // free (reserve)
  409.             $content .= str_repeat ("0", 11); 
  410.             // sender's bank code
  411.             $content .= str_pad ($exchange['sender_bank_code'], 8, "0", STR_PAD_LEFT); 
  412.             // sender's account number
  413.             $content .= str_pad ($exchange['sender_account_number'], 10, "0", STR_PAD_LEFT); 
  414.             // amount
  415.             $content .= str_pad ($exchange['amount'], 11, "0", STR_PAD_LEFT); 
  416.             // free (reserve)
  417.             $content .= str_repeat (" ", 3); 
  418.             // receiver's name
  419.             $content .= str_pad ($exchange['receiver_name'], 27, " ", STR_PAD_RIGHT); 
  420.             // delimitation
  421.             $content .= str_repeat (" ", 8); 
  422.             // sender's name
  423.             $content .= str_pad ($exchange['sender_name'], 27, " ", STR_PAD_RIGHT); 
  424.             // first line of purposes
  425.             $content .= str_pad ($first_purpose, 27, " ", STR_PAD_RIGHT); 
  426.             // currency (1 = Euro)
  427.             $content .= "1"; 
  428.             // free (reserve)
  429.             $content .= str_repeat (" ", 2); 
  430.             // amount of additional parts
  431.             $content .= str_pad ($additional_parts_number, 2, "0", STR_PAD_LEFT);
  432.  
  433.             if (count($additional_parts) > 0) {
  434.                 for ($index = 1;$index <= 2;$index++) {
  435.                     if (count($additional_parts) > 0) {
  436.                         $additional_part = array_shift($additional_parts);
  437.                     } else {
  438.                         $additional_part = array("type" => "  ",
  439.                             "content" => ""
  440.                             );
  441.                     }
  442.                     // type of addional part
  443.                     $content .= $additional_part['type']; 
  444.                     // additional part content
  445.                     $content .= str_pad ($additional_part['content'], 27, " ", STR_PAD_RIGHT);
  446.                 }
  447.                 // delimitation
  448.                 $content .= str_repeat (" ", 11);
  449.             }
  450.  
  451.             for ($part = 3;$part <= 5;$part++) {
  452.                 if (count($additional_parts) > 0) {
  453.                     for ($index = 1;$index <= 4;$index++) {
  454.                         if (count($additional_parts) > 0) {
  455.                             $additional_part = array_shift($additional_parts);
  456.                         } else {
  457.                             $additional_part = array("type" => "  ",
  458.                                 "content" => ""
  459.                                 );
  460.                         }
  461.                         // type of addional part
  462.                         $content .= $additional_part['type']; 
  463.                         // additional part content
  464.                         $content .= str_pad ($additional_part['content'], 27, " ", STR_PAD_RIGHT);
  465.                     }
  466.                     // delimitation
  467.                     $content .= str_repeat (" ", 12);
  468.                 }
  469.             }
  470.         }
  471.  
  472.         /**
  473.          * data record E
  474.          */
  475.  
  476.         // record length (128 bytes)
  477.         $content .= str_pad ("128", 4, "0", STR_PAD_LEFT); 
  478.         // record type
  479.         $content .= "E"; 
  480.         // free (reserve)
  481.         $content .= str_repeat (" ", 5); 
  482.         // number of records type C
  483.         $content .= str_pad (count($this->exchanges), 7, "0", STR_PAD_LEFT); 
  484.         // free (reserve)
  485.         $content .= str_repeat ("0", 13); 
  486.         // sum of account numbers
  487.         $content .= str_pad ($sum_account_numbers, 17, "0", STR_PAD_LEFT); 
  488.         // sum of bank codes
  489.         $content .= str_pad ($sum_bank_codes, 17, "0", STR_PAD_LEFT); 
  490.         // sum of amounts
  491.         $content .= str_pad ($sum_amounts, 13, "0", STR_PAD_LEFT); 
  492.         // delimitation
  493.         $content .= str_repeat (" ", 51);
  494.  
  495.         return $content;
  496.     }
  497.  
  498.     /**
  499.     * Writes the DTA file.
  500.     *
  501.     * @param  string  $filename Filename.
  502.     * @access public
  503.     * @return boolean
  504.     */
  505.     function saveFile($filename)
  506.     {
  507.         $content = $this->getFileContent();
  508.  
  509.         $Dta_fp = @fopen($filename, "w");
  510.         if (!$Dta_fp) {
  511.             $result = false;
  512.         } else {
  513.             $result = @fwrite($Dta_fp, $content);
  514.             @fclose($Dta_fp);
  515.         }
  516.  
  517.         return $result;
  518.     }
  519. }
  520.  
  521. ?>